<template>
  <div style="width: 100%; height: 100%">
    <el-input
      ref="searchInput"
      v-model="searchValue"
      placeholder="请输入搜索内容"
      :style="{
        width: '100%',
        margin: '8px 0px',
      }"
      clearable
      @clear="handleSearch"
      @change="handleSearch"
    />

    <template v-if="!loading">
      <RecycleScroller
        v-if="visibleData.length > 0"
        :style="{
          height: 'calc(100% - 32px - 16px)',
          'scroll-behavior': 'smooth',
        }"
        :key-field="nodeKey"
        :items="visibleData"
        :item-size="itemSize"
        :buffer="buffer"
        v-slot="{ item }"
      >
        <div
          class="virtual-tree__list-view"
          :style="{
            paddingLeft: 18 * item.level + 'px',
            height: itemSize + 'px',
            backgroundColor: item.highlight ? 'yellow' : '',
          }"
        >
          <img
            v-if="
              item.children &&
              item.children.filter((item) => item.filterVisible).length
            "
            :style="{
              width: '12px',
              marginRight: '4px',
              transform: item.expand ? 'rotate(90deg)' : 'rotate(0deg)',
            }"
            src="../img/tree-triangle.svg"
            @click="toggleExpand(item)"
          />
          <span
            v-else
            style="width: 18px; display: inline-block; flex-shrink: 0"
          />

          <img
            v-if="item.modelVisible"
            @click="onVisibleModel(item, false)"
            style="width: 18px"
            src="../img/open-eye.svg"
          />
          <img
            v-else
            @click="onVisibleModel(item, true)"
            style="width: 18px"
            src="../img/close-eye.svg"
          />

          <span @click="treeClick(item)" class="virtual-tree__title">{{
            showTextFun(item)
          }}</span>
        </div>
      </RecycleScroller>
      <div v-else class="not-found">暂无数据</div>
    </template>
    <template v-else>
      <div class="loading">
        <i class="fa fa-spinner put-load-loop" style="margin-right: 4px" />
        <span>加载中</span>
      </div>
    </template>
  </div>
</template>

<script>
import { RecycleScroller } from "vue-virtual-scroller";
import { ElInput, ElCheckbox } from "element-plus";

export default {
  components: {
    RecycleScroller,
    ElInput,
    ElCheckbox,
  },
  props: {
    selectKeys: {
      type: Array,
      default: () => [],
    },
    data: {
      type: Object,
      default: () => {
        return {};
      },
    },
    nodeKey: {
      type: String,
      default: "uuid",
    },
    showTextFun: {
      type: Function,
      default: (item) => {
        return item.name;
      },
    },
  },
  emits: ["visibleModel", "setSelectModel"],
  data() {
    return {
      searchValue: "",
      loading: false,
      itemSize: 36,
      buffer: 100,
      defaultExpandLevel: 0,
      dataList: [],

      nodeMap: null,
    };
  },
  computed: {
    visibleData() {
      return this.dataList.filter((i) => i.visible && i.filterVisible);
    },
  },
  watch: {
    selectKeys: {
      handler(newVal, oldVal) {
        console.log("key更新");
        this.selectToggleExpand(newVal);
      },
      deep: true,
      immediate: true,
    },
    data(val) {
      this.initData(val);
    },
  },
  mounted() {
    this.initData(this.data);
  },
  methods: {
    getUuid() {
      function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
          .toString(16)
          .substring(1);
      }
      return (
        s4() +
        s4() +
        "-" +
        s4() +
        "-" +
        s4() +
        "-" +
        s4() +
        "-" +
        s4() +
        s4() +
        s4()
      );
    },
    initData(tree) {
      this.nodeMap = new Map();
      if (!(tree && Object.keys(tree).length)) return;
      this.stateTree = [];
      this.dataList = [];
      this.searchValue = "";
      this.loading = true;
      let data = tree;
      setTimeout(() => {
        this.loading = false;
        this.dataList = this.handleTree(data, [], 0, true);
      }, 500);
    },

    handleTree(obj, list, level, visible) {
      obj[this.nodeKey] = obj[this.nodeKey] || this.getUuid();
      obj.level = level;
      obj.expand = level <= this.defaultExpandLevel;
      obj.originExpand = obj.expand;
      obj.visible = visible || false;
      obj.originVisible = obj.visible;
      obj.filterVisible = true;

      obj.modelVisible = true;
      this.nodeMap.set(obj.uuid, obj);
      if (obj) list.push(obj);
      if (obj.children && obj.children.length) {
        obj.children.forEach((item) => {
          item.parentNode = obj;
          // item.parentIds = [obj[this.nodeKey]].concat((obj.parentIds || []))
          item.parentIds = (obj.parentIds || []).concat([obj[this.nodeKey]]);
          this.handleTree(item, list, level + 1, obj.expand);
        });
      }
      return list;
    },

    handleSearch() {
      const value = this.searchValue;
      if (!value) {
        this.dataList.forEach((i) => {
          i.visible = i.originVisible;
          i.expand = i.originExpand;
          i.filterVisible = true;
        });
      } else {
        let parentIds = [];
        this.dataList.forEach((item) => {
          item.visible = true;
          item.expand = true;
          item.filterVisible =
            this.showTextFun(item).toLowerCase().indexOf(value.toLowerCase()) >
            -1;
          if (item.filterVisible) {
            parentIds = parentIds.concat(item.parentIds || []);
          }
        });
        parentIds = Array.from(new Set(parentIds));
        this.dataList.forEach((item) => {
          if (parentIds.includes(item[this.nodeKey])) {
            item.visible = true;
            item.expand = true;
            item.filterVisible = true;
          }
        });
      }
    },

    selectToggleExpand(vals) {
      this.dataList.forEach((item) => {
        item.highlight = false;
      });

      let arr = [];
      vals.forEach((val) => {
        let obj = this.nodeMap.get(val);
        obj.highlight = true;
        arr = arr.concat(obj.parentIds || []);
      });

      arr = Array.from(new Set(arr));
      arr.sort((a, b) => {
        return this.nodeMap.get(b).level - this.nodeMap.get(a).level;
      });
      arr.forEach((val) => {
        let obj = this.nodeMap.get(val);
        console.log("level", obj.level);
        obj.expand = true;
        obj.visible = true;
        let highlightSelectNum = 0;
        obj.children.forEach((child) => {
          child.highlight && highlightSelectNum++;
          child.visible = true;
        });
        if (highlightSelectNum === obj.children.length) {
          obj.highlight = true;
        }
        console.log("调用中");
      });
      console.log("调用完成");
    },
    selectToggleExpandAll() {
      this.dataList.forEach((item) => {
        item.visible = true;
        item.expand = true;
      });
    },
    treeClick(item) {
      this.$emit("setSelectModel", item[this.nodeKey]);
    },

    // 树形的勾选眼睛事件
    onVisibleModel(item, visible) {
      item.modelVisible = visible;
      this.$emit("visibleModel", item, visible);
      this.updateRecursionNodeKey(item.children, "modelVisible", visible);
      this.updateRecursionNodeKeyUp(item, "modelVisible");
    },
    /**
     * 展开方法
     * @param item
     */
    toggleExpand(item) {
      if (item.expand) {
        item.expand = false;
        this.updateRecursionNodeKey(item.children, "visible", false);
      } else {
        item.expand = true;
        this.updateRecursionNodeKey(item.children, "visible", true);
      }
    },
    //向下递归更新节点
    updateRecursionNodeKey(children, key, status) {
      children = children || [];
      children.forEach((node) => {
        node[key] = status;
        if (node.children && node.children.length) {
          if (key === "visible") {
            if (status) {
              if (node.expand) {
                // 子节点是展开的，此时需要更新子节点的visible属性
                this.updateRecursionNodeKey(node.children, key, status);
              }
            } else {
              // 关闭时把所有子节点都关闭
              node.expand = status;
              this.updateRecursionNodeKey(node.children, key, status);
            }
          }
          if (key === "modelVisible") {
            this.updateRecursionNodeKey(node.children, key, status);
          }
        }
      });
    },
    // 向上递归更新节点
    updateRecursionNodeKeyUp(data, key) {
      const parentNode = data.parentNode;
      if (parentNode) {
        if (key === "modelVisible") {
          let filterVisibleNum = parentNode.children.findIndex(
            (item) => item.modelVisible
          );
          parentNode.modelVisible = filterVisibleNum > -1;
        }
        this.updateRecursionNodeKeyUp(parentNode);
      }
    },
  },
};
</script>

<style lang="less" scoped>
.virtual-tree__list-view {
  cursor: pointer;
}
/deep/ .vue-recycle-scroller__item-wrapper {
  overflow-x: auto;
  height: 100%;
}

.virtual-tree {
  &__list-view {
    position: relative;
    margin: 0;
    display: flex;
    white-space: nowrap;
  }
  &__arrow {
    width: 18px;
    height: 100%;
    display: inline-block;
    cursor: pointer;
    text-align: center;
    i {
      transition: all 0.2s ease-in-out;
      color: #d0d2d4;
      font-size: 16px;
    }
  }
  /deep/ .put-checkbox-wrapper {
    margin: 0 4px;
  }
  &__title {
    flex: 1;
    padding: 0 4px;
    height: 100%;
    display: flex;
    align-items: center;
  }
}
</style>
